
import { ValuePair } from './models/value-pair';
import { defaultToString } from '../util';
/**
 * 字典：一个以[键, 值]形式来存储唯一值的数据结构。(null和undefined不可以用作键或值)
 * 而集合是关注值本身并把它当做主要元素，也就是[值, 值]的形式
 * es6新增了Map类，跟这个Dictionary功能一样，区别就是values和keys返回的是迭代器还有专门的size字段
 * es6还新增了Set和Map的弱化版本，WeakSet和WeakMap，区别就是后者没有entries、keys、values等方法并且只能用对象作为键
 */
export default class Dictionary<K, V> {
    private _table: { [K: string]: ValuePair<K, V> };
    private _toStrFn: (key: K) => string;
    constructor(toStrFn: (key: K) => string = defaultToString) {
        this._toStrFn = toStrFn;
        this._table = {};
    }
    /**
     * 向字典中添加新元素。如果key已经存在则会被新的value覆盖
     */
    public set(key: K, value: V): boolean {
        if (key != null && value != null) { // null和undefined不可以用作键或值
            const keyStr: string = this._toStrFn(key); // 将键转为字符串
            this._table[keyStr] = new ValuePair(key, value); // 以对象的形式存储
            return true;
        }
        return false;
    }
    /**
     * 通过以键值作为参数查找对应的值并返回
     */
    public get(key: K): V {
        if (key != null) {
            const keyStr: string = this._toStrFn(key);
            const valuePair: ValuePair<K, V> = this._table[keyStr];
            return !valuePair ? undefined : valuePair.value;
        }
        return undefined;
    }
    /**
     * 如果某个键值存在于该字典中返回true，否则返回false
     */
    public hasKey(key: K): boolean {
        return !this.get(key) ? false : true;
    }
    /**
     * 通过键来删除字典中对应的键值对
     */
    public remove(key: K): boolean {
        if (this.hasKey(key)) {
            delete this._table[this._toStrFn(key)];
            return true;
        }
        return false;
    }
    /**
     * 将字典中所有[键, 值]对返回
     */
    public keyValues(): Array<ValuePair<K, V>> {
        return Object.values(this._table);
    }
    /**
     * 将字典所包含的所有数值以数组形式返回
     */
    public values(): V[] {
        return this.keyValues().map((valuePair: ValuePair<K, V>) => valuePair.value);
    }
    /**
     * 将字典所包含的所有键名以数组的形式返回
     */
    public keys(): K[] {
        return this.keyValues().map((valuePair: ValuePair<K, V>) => valuePair.key);
    }
    /**
     * 迭代字典中所有的键值对(只要有一项不符合callBackFn就中断迭代)
     */
    public forEach(callBackFn: (key: K, value: V) => any) {
        const valuePair = this.keyValues();
        for (let i = 0, le = valuePair.length; i < le; i ++) {
            const result = callBackFn(valuePair[i].key, valuePair[i].value);
            if (result === false) {
                break;
            }
        }
    }
    /**
     * 返回字典所包含元素的数量
     */
    public size(): number {
        return Object.keys(this._table).length;
    }
    /**
     * 字典是否为空
     */
    public isEmpty(): boolean {
        return this.size() === 0;
    }
    /**
     * 清空字典
     */
    public clear() {
        this._table = {};
    }
    /**
     * 返回字典的字符串形式
     */
    public toString(): string {
        if (this.isEmpty()) {
            return '';
        }
        const valuePair = this.keyValues();
        let objString = `${valuePair[0].toString()}`;
        for (let i = 1; i < valuePair.length; i ++) {
            objString = `${objString},${valuePair[i].toString()}`;
        }
        return objString;
    }
}
